package com.future.service;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.future.common.exception.AppException;
import com.future.dao.SysUserDao;
import com.future.model.SysUser;
import com.jfinal.aop.Inject;
import com.jfinal.kit.PropKit;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;

import net.minidev.json.JSONObject;

public class AuthenticationService {
	@Inject
	private SysUserService sysUserService;
	
	@Inject
	private SysUserDao sysUserDao;
	/**
	 * 根据用户名密码获取token
	 * 
	 * @param userName
	 * @param password
	 * @return
	 */
	public String getToken(String userName, String password) {
		this.sysUserService.validateUser(userName, password);
		SysUser user = this.sysUserDao.findUserByUsername(userName);
		Map<String, Object> payloadMap = new HashMap<String, Object>();
		Long exp = new Date().getTime() + PropKit.getLong("tokenexp") * 1000;
		payloadMap.put("exp", exp);
		payloadMap.put("userid", user.getUserid());
		String token = this.creatToken(payloadMap);
		return token;
	}

	/**
	 * 刷新token,防止token因为超期而无法使用
	 * 
	 * @param token
	 * @return
	 */
	public String refreshToken(String token) {
		JSONObject result = this.parseToken(token);
		String userid = result.getAsString("userid");
		Long exp = new Date().getTime() + PropKit.getLong("tokenexp") * 1000;
		Map<String, Object> payloadMap = new HashMap<String, Object>();
		payloadMap.put("exp", exp);
		payloadMap.put("userid", userid);
		String tokennew = this.creatToken(payloadMap);
		return tokennew;
	}

	private String creatToken(Map<String, Object> payloadMap) {
		try {
			String secret = PropKit.get("tokensecret");
			// 先建立一个头部Header
			/**
			 * JWSHeader参数：1.加密算法法则,2.类型，3.。。。。。。。 一般只需要传入加密算法法则就可以。 这里则采用HS256
			 *
			 * JWSAlgorithm类里面有所有的加密算法法则，直接调用。
			 */
			JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);

			// 建立一个载荷Payload
			Payload payload = new Payload(new JSONObject(payloadMap));

			// 将头部和载荷结合在一起
			JWSObject jwsObject = new JWSObject(jwsHeader, payload);

			// 建立一个密匙
			JWSSigner jwsSigner = new MACSigner(secret.getBytes());

			// 签名
			jwsObject.sign(jwsSigner);

			// 生成token
			return jwsObject.serialize();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("创建token失败!");
		}

	}

	/**
	 * 检验token的合法性，并获取相关的信息
	 * 
	 * @return
	 * @throws JOSEException
	 * @throws ParseException
	 */
	public JSONObject parseToken(String token) {
		Map<String, Object> validMap = this.parse(token);
		JSONObject jsonObject = null;
		int i = (int) validMap.get("Result");
		// 解析成功
		if (i == 0) {
			jsonObject = (JSONObject) validMap.get("data");
			// 解析异常
		} else if (i == 1) {
			throw new AppException("Authentication Failed!");
			// token过期
		} else if (i == 2) {
			throw new AppException("Token Expired!");
		}
		return jsonObject;
	}

	private Map<String, Object> parse(String token) {
		try {
			String secret = PropKit.get("tokensecret");
			// 解析token
			JWSObject jwsObject = JWSObject.parse(token);

			// 获取到载荷
			Payload payload = jwsObject.getPayload();

			// 建立一个解锁密匙
			JWSVerifier jwsVerifier = new MACVerifier(secret.getBytes());

			Map<String, Object> resultMap = new HashMap<>();
			// 判断token
			if (jwsObject.verify(jwsVerifier)) {
				resultMap.put("Result", 0);
				// 载荷的数据解析成json对象。
				JSONObject jsonObject = payload.toJSONObject();
				resultMap.put("data", jsonObject);

				// 判断token是否过期
				if (jsonObject.containsKey("exp")) {
					Long expTime = Long.valueOf(jsonObject.get("exp").toString());
					Long nowTime = new Date().getTime();
					// 判断是否过期
					if (nowTime > expTime) {
						// 已经过期
						resultMap.clear();
						resultMap.put("Result", 2);

					}
				}
			} else {
				resultMap.put("Result", 1);
			}
			return resultMap;

		} catch (Exception e) {
			e.printStackTrace();
			throw new AppException("认证失败,token异常!");
		}

	}

}
